import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import CryptoJS from 'crypto-js'
import { cloneDeep, merge } from 'lodash'
import qs from 'qs'
import { closeToast, showFailToast, showLoadingToast } from 'vant'

import { ResultData } from '@/typings/types'

const requestList = new Map() // 请求列表

class Request {
  private axiosInstance: AxiosInstance
  static defaultOptions = {
    method: 'post',
    headers: { 'content-type': 'application/json' },
    timeout: 10000,
    pem: false,
    sld: false // 是否loading
  }
  constructor() {
    this.axiosInstance = axios.create()
    this.setupInterceptors()
  }
  /**
   * @description 设置拦截器
   */
  setupInterceptors() {
    this.axiosInstance.interceptors.request.use(
      config => {
        return config
      },
      (err: AxiosError) => {
        return err
      }
    )
    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<ResultData>) => {
        return response
      },
      (err: AxiosError) => {
        return Promise.reject(err)
      }
    )
  }
  /**
   * http请求函数
   * @param {请求配置} config
   */
  request(config: AxiosRequestConfig): Promise<ResultData> {
    const requestConfigs = merge({}, Request.defaultOptions, cloneDeep(config))
    const { url, method, headers, data, pem, sld } = requestConfigs
    requestConfigs['headers'] = this.getheaders(headers, data)
    requestConfigs[method.toLowerCase() === 'get' ? 'params' : 'data'] = data

    const key = [url, method, qs.stringify(data)].join('&')
    if (requestList.has(key)) {
      const resData = cloneDeep(requestList.get(key))
      return Promise.resolve(resData)
    }

    return new Promise((resolve, reject) => {
      if (sld) {
        showLoadingToast({
          message: '加载中...',
          duration: 10000,
          forbidClick: true
        })
      }
      this.axiosInstance
        .request(requestConfigs)
        .then(res => {
          if (res.data.code === 200) {
            const resData = cloneDeep(res.data)
            requestList.set(key, resData)
            closeToast()
            resolve(res.data)
          } else if (res.data.code === 3401) {
            localStorage.clear()
            window.location.href = '/login' // 跳转登录
            // location.reload()
          } else {
            this.errorHandler(res, resolve, pem)
          }
        })
        .catch(err => {
          this.errorHandler(err, reject, pem)
        })
        .finally(() => {
          setTimeout(() => {
            requestList.delete(key)
          }, 1000)
        })
    })
  }
  /**
   * 接口错误处理函数
   * @param {接口返回数据} res
   * @param {Promise错误处理函数} callback
   * @param {处理错误: true 由页面自行处理 false 统一拦截处理} pem
   */
  errorHandler(res: AxiosResponse, callback: (arg: any) => void, pem: boolean) {
    if (!pem) {
      const message = res.hasOwnProperty('data') ? res.data.msg : res.message
      showFailToast({
        message,
        duration: 3000
      })
    } else {
      callback(res)
    }
  }
  /**
   * 请求头部信息
   * @param {头部信息} headers
   * @param {接口入参} data
   */
  getheaders(headers: object, data: object) {
    const reqtime = Date.now()
    const str = 'jwhuang' + reqtime + '|' + JSON.stringify(data)
    const sign = CryptoJS.MD5(str).toString()
    const Authorization = localStorage.getItem('token') || ''
    const target = {
      Authorization,
      reqtime,
      sign
    }
    return merge({}, target, headers)
  }
}

const request = (opt: AxiosRequestConfig) => new Request().request(opt)

export default request
